package com.spring;

import com.spring.annotation.Autowired;
import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import com.spring.annotation.Scope;
import com.spring.constant.CommonConstants;
import com.spring.util.Assert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Althumic Wei
 * @date 2023-07-30 17:02
 **/
@Slf4j
public class WeiApplicationContext {
    public static final String IMPL = "Impl";
    private Class configClass;

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    private Map<String, Object> singletonBeanMap = new HashMap<>();
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public WeiApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 1、扫描
        scan();

        // 2、createSingletonBean
        createSingletonBean();
    }

    private void createSingletonBean() {
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            BeanDefinition beanDefinition = beanDefinitionEntry.getValue();
            if (CommonConstants.SCOPE_SINGLETON.equals(beanDefinition.getScope())) {
                singletonBeanMap.put(beanName, createBean(beanName, beanDefinition));
            }
        }
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class beanClass = beanDefinition.getType();
        Object instance = null;
        try {
            instance = beanClass.getConstructor().newInstance();

            // 处理依赖注入
            for (Field field : beanClass.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    field.set(instance, getBean(field.getName()));
                }
            }

            // aware
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            // post before
            for (BeanPostProcessor afterPostProcessor : beanPostProcessorList) {
                instance = afterPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // initializingBean 方法
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // post after
            for (BeanPostProcessor afterPostProcessor : beanPostProcessorList) {
                instance = afterPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            log.error("create instance exception: " + e);
        } catch (Exception e) {
            log.error("exception: " + e);
        }
        return instance;
    }

    private void scan() {
        // 配置类使用注解扫描指定的路径，无注解则不处理
        if (!configClass.isAnnotationPresent(ComponentScan.class)) {
            return;
        }

        ComponentScan scanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        String path = scanAnnotation.value();
        path = path.replace(".", "/");
        ClassLoader classLoader = WeiApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);
        File directory = new File(resource.getFile());
        if (!directory.isDirectory()) {
            return;
        }

        List<File> classFileList = getFileList(directory);
        for (File classFile : classFileList) {
            String absolutePath = classFile.getAbsolutePath();
            String classFullName = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class")).replace(File.separator, ".");
            try {
                Class clazz = classLoader.loadClass(classFullName);
                // 只处理带Component注解的类（既被spring托管）
                if (!clazz.isAnnotationPresent(Component.class)) {
                    continue;
                }

                // 处理BeanPostProcessor
                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                    beanPostProcessorList.add((BeanPostProcessor) clazz.getConstructor().newInstance());
                }

                // 封装BeanDefinition
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setType(clazz);

                // 单例/多例（原型）
                if (clazz.isAnnotationPresent(Scope.class)) {
                    Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
                    beanDefinition.setScope(scopeAnnotation.value());
                } else {
                    beanDefinition.setScope(CommonConstants.SCOPE_SINGLETON);
                }

                Component componentAnnotation = (Component) clazz.getAnnotation(Component.class);
                String componentValue = componentAnnotation.value();
                // 注解没有指定名称 则类名首字母小写
                String beanName = StringUtils.isNotBlank(componentValue) ? componentValue : Introspector.decapitalize(clazz.getSimpleName());
                if (beanName.contains(IMPL)) {
                    beanName = beanName.replace(IMPL, "");
                }
                beanDefinitionMap.put(beanName, beanDefinition);
            } catch (ClassNotFoundException | NoSuchMethodException e) {
                log.error("load class exception: " + e);
            } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
                log.error("scan BeanPostProcessor exception: " + e);
            }
        }
    }

    private List<File> getFileList(File directory) {
        List<File> fileList = new ArrayList<>();
        for (File classFile : directory.listFiles()) {
            if (classFile.isDirectory()) {
                fileList.addAll(getFileList(classFile));
            } else {
                fileList.add(classFile);
            }
        }
        return fileList;
    }

    public Object getBean(String beanName) {
        Assert.isTrue(beanDefinitionMap.containsKey(beanName), beanName + " is not find......");
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 单例从单例缓存里取，多例则新建
        if (CommonConstants.SCOPE_SINGLETON.equals(beanDefinition.getScope())) {
            Object instance = singletonBeanMap.get(beanName);
            if (instance == null) {
                instance = createBean(beanName, beanDefinition);
                singletonBeanMap.put(beanName, instance);
            }
            return instance;
        }
        return createBean(beanName, beanDefinition);
    }
}
